package com.efounder.chat.utils;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils;

import com.efounder.chat.zxing.decoding.DecodeFormatManager;
import com.efounder.chat.zxing.qrcode.Bmp2YUVUtil;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.DecodeHintType;
import com.google.zxing.FormatException;
import com.google.zxing.NotFoundException;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.utilcode.util.LogUtils;

import java.util.Hashtable;
import java.util.Vector;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;

/**
 * 识别二维码工具类
 *
 * @author YQS 2018/08/04
 */
public class IdentifyQrCodeManager {
    private CompositeDisposable compositeDisposable;


    public void scanImage(final String path, final ScanCallBack scanCallBack) {

        Disposable disposable = Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                Result result = scanningImage(path);
                String scanResult = "";
                if (result != null) {
                    scanResult = result.getText();
                }
                emitter.onNext(scanResult);
                emitter.onComplete();
            }
        }).subscribeOn(Schedulers.io()).doOnSubscribe(new Consumer<Disposable>() {
            @Override
            public void accept(Disposable disposable) throws Exception {
            }
        }).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Exception {
                LogUtils.d("图片qr识别结果：" + s);
                if ("".equals(s)) {
                    if (scanCallBack != null) {
                        scanCallBack.scanFail();
                    }
                } else {
                    if (scanCallBack != null) {
                        scanCallBack.scanSuccess(path, s);
                    }
                }

            }
        }, new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) throws Exception {
//                ToastUtil.showToast(AppContext.getInstance(), "获取私钥失败，请重试");
                if (scanCallBack != null) {
                    scanCallBack.scanFail();
                }
            }
        });

        addDisposable(disposable);
    }


    /**
     * 扫描二维码图片的方法
     *
     * @param path
     * @return
     */
    public Result scanningImage(String path) {
        if (TextUtils.isEmpty(path)) {
            return null;
        }
        Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
        hints.put(DecodeHintType.CHARACTER_SET, "UTF8"); //设置二维码内容的编码
        // 可以解析的编码类型
        Vector<BarcodeFormat> decodeFormats = new Vector<BarcodeFormat>();
        // 这里设置可扫描的类型
        decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
        decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
        hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true; // 获取新的大小
        Bitmap scanBitmap = BitmapFactory.decodeFile(path, options);
        if (options.outHeight > 5000) {
            return null;
        }
        options.inJustDecodeBounds = false; // 获取新的大小
        for (int i = 1; i < 5; i++) {
            options.inSampleSize = i;

            scanBitmap = BitmapFactory.decodeFile(path, options);
//                //方法1
//            RGBLuminanceSource source = new RGBLuminanceSource(scanBitmap);
//            BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
//            try {
//                QRCodeReader reader = new QRCodeReader();
//                return reader.decode(bitmap1, hints);
//            } catch (NotFoundException e) {
//                e.printStackTrace();
//            } catch (ChecksumException e) {
//                e.printStackTrace();
//            } catch (FormatException e) {
//                e.printStackTrace();
//            }
            //方法2 比方法一有效 参考 http://www.imooc.com/article/22919

            // 2.塞给zxing进行decode
            try {
                // 1.将bitmap的RGB数据转化成YUV420sp数据
                byte[] bmpYUVBytes = Bmp2YUVUtil.getBitmapYUVBytes(scanBitmap);
                return decodeYUVByZxing(bmpYUVBytes, scanBitmap.getWidth(), scanBitmap.getHeight(), hints);
            } catch (FormatException e) {
                e.printStackTrace();
            } catch (ChecksumException e) {
                e.printStackTrace();
            } catch (NotFoundException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
            //裁剪下半部分图片再试一次
            scanBitmap = BitmapFactory.decodeFile(path, options);
            scanBitmap = Bitmap.createBitmap(scanBitmap, 0, scanBitmap.getHeight() * 3 / 5, scanBitmap.getWidth(), scanBitmap.getHeight() * 2 / 5);
            byte[] bmpYUVBytes = Bmp2YUVUtil.getBitmapYUVBytes(scanBitmap);
            // 2.塞给zxing进行decode

            LogUtils.i("裁剪下半部分图片后继续识别");
            return decodeYUVByZxing(bmpYUVBytes, scanBitmap.getWidth(), scanBitmap.getHeight(), hints);
        } catch (FormatException e) {
            e.printStackTrace();
        } catch (ChecksumException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static Result decodeYUVByZxing(byte[] bmpYUVBytes, int bmpWidth, int bmpHeight, Hashtable<DecodeHintType, Object> hints) throws FormatException, ChecksumException, NotFoundException {
        String zxingResult = "";
        // Both dimensions must be greater than 0
        if (bmpHeight > 5000) {
            //长图不进行识别处理
            return null;
        }
        if (null != bmpYUVBytes && bmpWidth > 0 && bmpHeight > 0) {

            PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(bmpYUVBytes, bmpWidth,
                    bmpHeight, 0, 0, bmpWidth, bmpHeight, true);
            BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
            Reader reader = new QRCodeReader();
            Result result = reader.decode(binaryBitmap, hints);
            return result;
        }
        return null;
    }

    public interface ScanCallBack {
        void scanSuccess(String filePath, String reult);

        void scanFail();
    }

    public void realease() {
        if (compositeDisposable != null) compositeDisposable.dispose();
    }

    void addDisposable(Disposable disposable) {
        if (compositeDisposable == null) {
            compositeDisposable = new CompositeDisposable();
        }
        if (disposable != null) {
            compositeDisposable.add(disposable);
        }

    }

}
